551 Lec 6 - Linking plots, maps in plotly and deployment on Heroku
Contents
551 Lec 6 - Linking plots, maps in plotly and deployment on Heroku¶
You need to download this notebook to view images.
Lecture learning goals¶
By the end of the lecture you will be able to:
Implement server side interactivity between plots
Work with geojson files in plotly
Create maps with interactivity
Setup and Heroku account and run the Heroku CLI
Prepare the necessary files for deployment
Push to Heroku’s repo and troubleshoot remote errors
Linking interactive plots¶
What is the return value from the selected data in a scatter plot?¶
Let’s start from where we left off last lecture.
library(dash)
library(dashCoreComponents)
library(dashHtmlComponents)
library(dashBootstrapComponents)
library(ggplot2)
library(plotly)
app <- Dash$new(external_stylesheets = dbcThemes$BOOTSTRAP)
app$layout(
dbcContainer(
list(
dccGraph(id='plot-area'),
htmlDiv(id='output-area'),
htmlBr(),
dccDropdown(
id='col-select',
options = msleep %>% colnames %>% purrr::map(function(col) list(label = col, value = col)),
value='bodywt')
)
)
)
app$callback(
output('plot-area', 'figure'),
list(input('col-select', 'value')),
function(xcol) {
p <- ggplot(msleep) +
aes(x = !!sym(xcol),
y = sleep_total,
color = vore,
text = name) +
geom_point() +
scale_x_log10() +
ggthemes::scale_color_tableau()
ggplotly(p, tooltip = 'text') %>% layout(dragmode = 'select')
}
)
app$callback(
output('output-area', 'children'),
list(input('plot-area', 'selectedData')),
function(selected_data) {
list(toString(selected_data))
}
)
app$run_server(debug = T)

The output from our selected data look like this when displayed as a string in the HTML div.
Another way to see these lists of names lists is to print them to the console inside your function with print(), and then they will look slightly different.
list(list(curveNumber = 1, pointNumber = 8, pointIndex = 8, x = 3.40602894496361, y = 3.9, text = "Asian elephant"), list(curveNumber = 1, pointNumber = 15, pointIndex = 15, x = 3.8230827965328, y = 3.3, text = "African elephant")), list(x = list(3.25242683180537, 4.1060910392025), y = list(2.4953125, 4.50625))
This is a list consisting of two named lists.
The first one (selected_data[[2]]) is pretty uninteresting for us
as it contains the x and y values of the rectangular selection
we created when dragging with the mouse:
list(x = list(3.25242683180537, 4.1060910392025), y = list(2.4953125, 4.50625))
The first one (selected_data[[1]]) has all the info of our selected points:
list(curveNumber = 1, pointNumber = 8, pointIndex = 8, x = 3.40602894496361, y = 3.9, text = "Asian elephant"),
list(curveNumber = 1, pointNumber = 15, pointIndex = 15, x = 3.8230827965328, y = 3.3, text = "African elephant")
One named list for the first point (Asian elephant) and one for the second point (African elephant).
curveNumberindicates which group/color the point belongs to.pointNumberandpointIndexunfortunately does not represent the row numbers in the data frame, but rather some ordering internal to plotly.The
xandyvalues represent the position of the point in our graph based on the x and y columns we chose.The
textvalue holds everything contained in our tooltip and if we include more than one value, the values will be separated by<br>in the returned string.
The most interesting values for us here are x, y, and text
since these can include features that are useful for filtering data in another callback.
If the feature you want to use for filtering is not on x or y,
include it in the tooltip, ideally as a single value.
Plotly does support a customdata attribute
which can be used to bass along arbitrary features
that you might want to filter on,
but for some reason this does not seem to work with ggplotly.
If you happen to be already using plotly directly for some part of your dashboard,
feel free to use customdata,
but for ggplotly use the strategy outlined above.
How to filter a dataframe based on the selected value¶
Now that we know which part of the selection we want,
how do we use it to filter our data?
First we need to extract just the part that we want from the named list.
For a single named list,
we could simply access that element with a name,
but since we might select multiple points
we need to map the name access to each point in the list.
In Python this would be a list comprehension,
and in R we can use purrr to map a selection to every item in the list
(similar to what we saw in setting the options for the dropdown)
In this example,
we want to grab the text field from the returned selected_data value:
selected_data[[1]] %>% purrr::map_chr('text')
## Asian elephant, African elephant
The return value is a vector of the type we specified in purrr.
If we wanted to grab an integer or float/double instead of a text string,
we would use map_int or map_dbl instead.
Now that we have these values, we can use them to filter our data frame with like so:
animal_names <- selected_data[[1]] %>% purrr::map_chr('text')
msleep %>% filter(name %in% animal_names)
If you don’t have a unique feature (like name here) to use for filtering,
you can create a “metadata/ID” column consisting of your dataframes rownames
and then assign this to the text and tooltip property,
so that you can use it to reference back to your dataframe.
With these changes, the calback would look like this:
app$callback(
output('output-area', 'children'),
list(input('plot-area', 'selectedData')),
function(selected_data) {
animal_names <- selected_data[[1]] %>% purrr::map_chr('text')
print(msleep %>% filter(name %in% animal_names))
toString(animal_names) # Only for printing the names in the div
}
)

You can see in the printed console output that we have filtered correctly

Plotting the selected data via another callback (a.k.a. server side interactivity)¶
To use these selected values to create a plot, we would set up a regular callback, “purrr out” the values we want, filter our dataframe and use thie data for plotting. We will also change the div output area for another plotting area, so that our app now looks like this:
library(dash)
library(dashCoreComponents)
library(dashHtmlComponents)
library(dashBootstrapComponents)
library(ggplot2)
library(plotly)
app <- Dash$new(external_stylesheets = dbcThemes$BOOTSTRAP)
app$layout(
dbcContainer(
list(
dccGraph(id='plot-area'),
dccGraph(id='bar-plot'),
htmlBr(),
dccDropdown(
id='col-select',
options = msleep %>% colnames %>% purrr::map(function(col) list(label = col, value = col)),
value='bodywt')
)
)
)
app$callback(
output('plot-area', 'figure'),
list(input('col-select', 'value')),
function(xcol) {
p <- ggplot(msleep) +
aes(x = !!sym(xcol),
y = sleep_total,
color = vore,
text = name) +
geom_point() +
scale_x_log10() +
ggthemes::scale_color_tableau()
ggplotly(p, tooltip = 'text') %>% layout(dragmode = 'select')
}
)
app$callback(
output('bar-plot', 'figure'),
list(input('plot-area', 'selectedData')),
function(selected_data) {
animal_names <- selected_data[[1]] %>% purrr::map_chr('text')
p <- ggplot(msleep %>% filter(name %in% animal_names)) +
aes(y = vore,
fill = vore) +
geom_bar(width = 0.6) +
ggthemes::scale_fill_tableau()
ggplotly(p, tooltip = 'text') %>% layout(dragmode = 'select')
}
)
app$run_server(debug = T)

More on ggplotly can be found in the docs and also in this separate resource, which goes some additional plotly functions we can use to control the ggplot objects.
Creating maps with plotly¶
There are a few different approaches to maps we could could we dashr,
including geom_df and leaflet,
and here we will use plotly’s map plotting functions.
We can use our own geojson files with plotly,
and just like for Altair,
they also supply data sets for the coutries of the world,
and the US states.
In fact,
the default in plotly’s choropleth function is to show a map of the world.
df <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv")
head(df)
| COUNTRY | GDP..BILLIONS. | CODE | |
|---|---|---|---|
| <chr> | <dbl> | <chr> | |
| 1 | Afghanistan | 21.71 | AFG |
| 2 | Albania | 13.40 | ALB |
| 3 | Algeria | 227.80 | DZA |
| 4 | American Samoa | 0.75 | ASM |
| 5 | Andorra | 4.80 | AND |
| 6 | Angola | 131.40 | AGO |
library(plotly)
plot_ly(df, type='choropleth')
Loading required package: ggplot2
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
These are zoomable by default and can be linked to datasets with country codes.
Plotly uses ~ to reference a variable/column name in the dataframe.
plot_ly(df, type='choropleth', locations=~CODE, z=~GDP..BILLIONS.)